/*
 * ArbitraryWait.cpp
 *
 *  Created on: Nov 6, 2011
 *      Author: mgibson
 */

#include "ArbitraryWait.h"
#include "WaitObject.h"

ArbitraryWait::ArbitraryWait() {
	pthread_mutex_init(&listlock, NULL);
	pthread_mutex_init(&waitlock, NULL);
	pthread_cond_init(&waitcond, NULL);
	pthread_rwlock_init(&transferlock, NULL);
	pthread_mutex_init(&gglock,NULL);
	pthread_cond_init(&ggcond,NULL);
	objs = NULL;
	numobjs = 0;
	errsig = -1;

	pthread_mutex_lock(&waitlock);
}

ArbitraryWait::~ArbitraryWait() {
	for (int i = 0; i < numobjs; i++)
		delete objs[i];
	delete[] objs;

	pthread_mutex_destroy(&listlock);
	pthread_mutex_destroy(&waitlock);
	pthread_cond_destroy(&waitcond);
	pthread_rwlock_destroy(&transferlock);
	pthread_mutex_destroy(&gglock);
	pthread_cond_destroy(&ggcond);
}

unsigned int ArbitraryWait::addWaitObject(int sockfd, char* buffer, int length) {
	WaitObject* newwait(sockfd, buffer, length);
	return AddWaitObject(newwait);
}

unsigned int ArbitraryWait::addWaitObject(pthread_mutex_t* mutex,
		pthread_cond_t* cond) {
	WaitObject* newwait(mutex, cond);
	return AddWaitObject(newwait);
}

unsigned int ArbitraryWait::addWaitObject(WaitObject* newwait) {
	pthread_mutex_lock(&listlock);
	returnInt = -1;
	if (numobjs + 1 < errsig) {
		WaitObject** newlist = new WaitObject*[numobjs + 1];
		for (int i = 0; i < numobjs; i++)
			newlist[i] = objs[i];
		newlist[numobjs] = newwait;
		numobjs++;
		delete[] objs;
		objs = newlist;
		returnInt = this->numobjs;
	}
	pthread_mutex_unlock(&listlock);
	return returnInt;
}

unsigned int ArbitraryWait::addPthreadWaitObject() {
	WaitObject* newwait(new pthread_mutex_t, new pthread_cond_t);
	return AddWaitObject(newwait);
}

void ArbitraryWait::deleteWaitObject(unsigned int index) {
	pthread_mutex_lock(&listlock);
	if (index < numobjs) {
		WaitObject** newlist = new WaitObject*[numobjs - 1];
		for (int i = 0; i < index; i++)
			newlist[i] = objs[i];
		for (int i = index; i < numobjs - 1; i++)
			newlist[i] = objs[i + 1];
	}
	pthread_mutex_unlock(&listlock);
}

unsigned int ArbitraryWait::wait() {
	pthread_mutex_lock(&listlock);

	pthread_t* threadList = new pthread_t[numobjs];
	unsigned int returnID = -1;
	bool masterBool = false;

	for (int i = 0; i < numobjs; i++) {
		objs[i]->ID = i;
		objs[i]->returnID = &returnID;
		objs[i]->masterBool = &masterBool;
		objs[i]->masterCond = waitcond;
		objs[i]->masterMutex = waitlock;
		pthread_mutex_lock(objs[i]->mutex);

		pthread_create(&threadList[i], NULL, &waiter,
				static_cast<void*> (objs[i]));
	}

	for (int i = 0; i < numobjs; i++) {
		pthread_cond_wait(objs[i]->cond, objs[i]->mutex);
		pthread_mutex_unlock(objs[i]->mutex);
	}

	pthread_cond_wait(&waitcond, &waitlock);
	masterBool = true;

	for (int i = 0; i < numobjs; i++) {
		if (returnID != IDs[i]) {
			pthread_mutex_lock(objs[i]->mutex);
			pthread_cond_signal(objs[i]->cond);
			pthread_mutex_unlock(objs[i]->mutex);
		}

		pthread_join(threadList[i], NULL);
	}

	pthread_mutex_unlock(&listlock);

	delete[] waitList;
	delete[] threadList;

	return returnID;

}

void* ArbitraryWait::waiter(void* waitObjVoid) {
	WaitObject* wait = static_cast<WaitObject*> (waitObjVoid);

	pthread_mutex_lock(wait->mutex);
	pthread_cond_signal(wait->cond);
	if (wait->type == PTHREAD) {
		wait->errno = pthread_cond_wait(wait->cond, wait->mutex);
		pthread_mutex_unlock(wait->mutex);
	}
	if (wait->type == READ) {
		wait->errno = recv(wait->sockfd, wait->buffer, wait->blength-1, 0);

	}

	if (!(*wait->masterBool)) {
		pthread_mutex_lock(wait->masterMutex);
		if(!(*wait->masterBool))
			*wait->returnID = wait->ID;
		pthread_cond_signal(wait->masterCond);
		pthread_mutex_unlock(wait->masterMutex);
	}

	pthread_exit(NULL);
	return NULL;
}

void ArbitraryWait::abandonButtonWait() {
	pthread_mutex_lock(&waitlock);
	pthread_cond_signal(&waitcond);
	pthread_mutex_unlock(&waitlock);
}

void ArbitraryWait::signalWait(pthread_mutex_t* mutex, pthread_cond_t* cond) {
	pthread_rwlock_wrlock(&transferlock);
	pthread_mutex_lock(&waitlock);
	pthread_mutex_lock(mutex);
	pthread_cond_signal(cond);
	pthread_mutex_unlock(mutex);
	pthread_mutex_unlock(&waitlock);
	pthread_rwlock_unlock(&transferlock);
}

void ArbitraryWait::signalWaitingObject(unsigned int index) {
	pthread_mutex_lock(&listlock);
	if (index < numobjs) {
		pthread_rwlock_wrlock(&transferlock);
		pthread_mutex_lock(&waitlock);
		pthread_mutex_lock(objs[index]->mutex);
		pthread_cond_signal(objs[index]->cond);
		pthread_mutex_unlock(objs[index]->mutex);
		pthread_mutex_unlock(&waitlock);
		pthread_rwlock_unlock(&transferlock);
	}
	pthread_mutex_unlock(&listlock);
}

void ArbitraryWait::giveLock() {
	pthread_rwlock_rdlock(&transferlock);
	pthread_mutex_lock(&gglock);
	pthread_mutex_unlock(&waitlock);
	pthread_cond_wait(&ggcond, &gglock);
	pthread_mutex_unlock(&gglock);
	pthread_rwlock_unlock(&transferlock);
}

void ArbitraryWait::getLock() {
	pthread_rwlock_rdlock(&transferlock);
	pthread_mutex_lock(&waitlock);
	pthread_mutex_lock(&gglock);
	pthread_cond_signal(&ggcond);
	pthread_mutex_unlock(&gglock);
	pthread_rwlock_unlock(&transferlock);
}
